package vip.xiaonuo.util;

import cn.hutool.core.lang.Console;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.stereotype.Component;
import vip.xiaonuo.core.exception.ServiceException;
import vip.xiaonuo.core.pojo.base.entity.BaseEntity;
import vip.xiaonuo.modular.customcode.entity.CustomCode;
import vip.xiaonuo.modular.customcode.enums.CustomCodeExceptionEnum;
import vip.xiaonuo.modular.customcode.service.CustomCodeService;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;

/**
 * @suthor: sxy
 * @date: 2022/6/24 16:20
 * @version: 1.0
 **/
@Component
public class AutoCode {

    private static final HashMap<Integer, String> codeMap = new HashMap<Integer, String>() {
        {
            put(0, "code");
            put(1, "work_order_no");
        }
    };
    private static final LocalDate localDate = LocalDate.now();


    /**
     * return: String
     * Author: sxy
     * Description: //TODO tableName 一定和数据库表名一样
     * Date: 10:02 2022/6/28
     * Param: tableName, iService, logo
     **/
    public static <T> String getCodeByService(String tableName, Class<? extends IService<T>> iService, int logo) {
        // TODO 判空
        if (ObjectUtil.isEmpty(tableName)) {
            throw new ServiceException(3, "Table name is null");
        }
        if (ObjectUtil.isEmpty(iService)) {
            throw new ServiceException(3, "ServiceImpl is null");
        }
        if (ObjectUtil.isEmpty(logo)) {
            throw new ServiceException(3, "logo is null");
        }
        // TODO 查询相关表的注释
        //SpringUtil.getBean(CustomCodeService.class).getInformationTable()
        // TODO 根据tableName查找表的编号规则
        final QueryWrapper<CustomCode> wrapper = new QueryWrapper<>();
        wrapper.eq("table_name", tableName);
        final CustomCode customCode = SpringUtil.getBean(CustomCodeService.class).getOne(wrapper);
        if (ObjectUtil.isEmpty(customCode) || ObjectUtil.isNull(customCode)) {
            throw new ServiceException(3, "此表无自定义编号规则！");
        }
        // TODO 根据customCode的自定义编码和时间查找数据库
        //  TODO 构建查询条件
        StringBuilder query = new StringBuilder();
        // TODO 一：根据时间规则获取今天的日期
        StringBuilder date = new StringBuilder();
        if (customCode.getTimeFormat() == 0) {
            date.append(localDate.getYear());
        }
        if (customCode.getTimeFormat() == 1) {
            date.append(localDate.getYear());
            date.append(localDate.getMonthValue());
        }
        if (customCode.getTimeFormat() == 2) {
            date.append(localDate.getYear());
            date.append(localDate.getMonthValue());
            date.append(localDate.getDayOfMonth());
        }
        // TODO 二：拼接前缀
        query.append(customCode.getEncode()).append(date);
        for (int i = 0; i < customCode.getSerialNum(); i++) {
            query.append("_");
        }
        // TODO 准备构建最终的code
        StringBuilder code = new StringBuilder();
        // TODO 前缀拼接
        code.append(customCode.getEncode()).append(date);
        // TODO 三：查询条件限制
        //      TODO 编号位数限制
        int length = 0;
        if (customCode.getEncode().isEmpty()) {
            length = date.length() + customCode.getSerialNum();
        } else {
            length = customCode.getEncode().length() + date.length() + 1;
        }
        // TODO 获取流水号的最大值
        String max = SpringUtil.getBean(CustomCodeService.class).getMax(tableName, codeMap.get(logo), customCode.getSerialNum(), length, query.toString() + customCode.getFreeField());
        // TODO 检查有无此规则下的编号
        if (ObjectUtil.isEmpty(max) || ObjectUtil.isNull(max)) {
            // TODO 若无，根据流水号位数生成生成几个0
            for (int i = 0; i < customCode.getSerialNum() - 1; i++) {
                code.append("0");
            }
            // TODO 最后拼接1
            code.append("1");
            // TODO 添加自由字段后直接返回（因为此规则下无编号，故一定唯一，无需检查）
            code.append(customCode.getFreeField());
            return code.toString();
        }
        // TODO 有编号，转int，+1
        Integer value = Integer.valueOf(max);
        value = value + 1;
        // TODO int转String
        final String valueOf = String.valueOf(value);
        // TODO 长度对比，0=无需添0且正确；>0=需要添0；<0=位数超限
        final int subNum = max.length() - valueOf.length();
        if (subNum > 0) {
            for (int i = 0; i < subNum; i++) {
                code.append("0");
            }
        }
        if (subNum < 0) {
            throw new ServiceException(3, "流水号以达到上限，请修改流水号位数!");
        }
        // TODO code拼接倒数第二块
        code.append(valueOf);
        // TODO 添加自由字段
        code.append(customCode.getFreeField());
        // TODO 校验code是否存在，不可修改前提下逻辑上应该为true
        final Boolean checkResult = checkCodeByFactory(iService, code.toString(), logo);
        if (checkResult) {
            return code.toString();
        } else {
            throw new ServiceException(3, "系统错误！请联系管理员！");
        }
    }

    /**
     * 批量获取编号 线程不安全
     *
     * @param tableName
     * @param iService
     * @param logo
     * @param limit
     * @param <T>
     * @return
     */
    public static <T> List<String> getCodeByService(String tableName, Class<? extends IService<T>> iService, String logo, int limit) {
        //  判空
        if (ObjectUtil.isEmpty(tableName)) {
            throw new ServiceException(CustomCodeExceptionEnum.TABLE_NAME_IS_NULL);
        }
        if (ObjectUtil.isEmpty(iService)) {
            throw new ServiceException(CustomCodeExceptionEnum.SERVER_IS_NULL);
        }
        if (ObjectUtil.isEmpty(logo)) {
            throw new ServiceException(CustomCodeExceptionEnum.LOGO_IS_NULL);
        }
        if (ObjectUtil.isNull(limit) || ObjectUtil.isEmpty(limit) || limit < 1) {
            limit = 1;
        }
        //  根据tableName查找表的编号规则
        final CustomCode customCode = SpringUtil.getBean(CustomCodeService.class).getOne(Wrappers.<CustomCode>lambdaQuery().eq(CustomCode::getTableName, tableName));
        if (ObjectUtil.isEmpty(customCode) || ObjectUtil.isNull(customCode)) {
            throw new ServiceException(CustomCodeExceptionEnum.NOT_RULE);
        }
        //  根据customCode的自定义编码、时间、自由字段查找数据库
        //   构建查询条件
        //              前缀 日期 流水号 后缀
        String query = "{}{}{}{}";
        //  前缀
        String prefix = "";
        if (ObjectUtil.isNotNull(customCode.getEncode()) || ObjectUtil.isNotEmpty(customCode.getEncode())) {
            prefix = customCode.getEncode();
        }
        //  后缀
        String suffix = "";
        if (ObjectUtil.isNotNull(customCode.getFreeField()) || ObjectUtil.isNotEmpty(customCode.getFreeField())) {
            suffix = customCode.getFreeField();
        }
        //  一：根据时间规则获取今天的日期
        StringBuilder date = new StringBuilder();
        if (customCode.getTimeFormat() == 0 || (customCode.getTimeFormat() > 0 && customCode.getTimeFormat() < 3)) {
            date.append(localDate.getYear());
        }
        if (customCode.getTimeFormat() == 1 || (customCode.getTimeFormat() > 1 && customCode.getTimeFormat() < 3)) {
            if (localDate.getMonthValue() < 10) {
                date.append(0);
            }
            date.append(localDate.getMonthValue());
        }
        if (customCode.getTimeFormat() == 2) {
            if (localDate.getDayOfMonth() < 10) {
                date.append(0);
            }
            date.append(localDate.getDayOfMonth());
        }
        //  准备构建流水号的code
        StringBuilder code = new StringBuilder();
        //  三：查询条件限制
        //       编号位数限制
        int length = customCode.getSerialNum();
        if (ObjectUtil.isNotEmpty(customCode.getEncode())) {
            length += customCode.getEncode().length();
        }
        if (ObjectUtil.isNotEmpty(date)) {
            length += date.length();
        }
        if (ObjectUtil.isNotEmpty(customCode.getFreeField())) {
            length += customCode.getFreeField().length();
        }
        int start = length - customCode.getSerialNum() + 1;
        StringBuilder serialNum = new StringBuilder();
        for (int i = 0; i < customCode.getSerialNum(); i++) {
            serialNum.append("_");
        }
        // 获取编号
        final List<String> codeList = SpringUtil.getBean(CustomCodeService.class).getCodeList(tableName, logo, start, customCode.getSerialNum(), length, StrUtil.format(query, prefix, date, serialNum, suffix), limit, prefix + date, suffix);
        // 获取编号最大值
        int max = Integer.parseInt(codeList.get(0).substring(start - 1, start + customCode.getSerialNum() - 1).trim()) - 1;
        // 获取当前流水号的极限数字
        StringBuilder maxSerialNum = new StringBuilder();
        maxSerialNum.append("1");
        for (int i = 0; i < customCode.getSerialNum(); i++) {
            maxSerialNum.append("0");
        }
        // 极限流水号
        int maxNum = Integer.parseInt(maxSerialNum.toString()) - 1;
        // 编号流水号超限校验
        // 需要生成的编号数量是否符合剩余的编号数量
        if ((maxNum - max) < limit) {
            throw new ServiceException(CustomCodeExceptionEnum.LIMIT_ERROR);
        }
//        if (Integer.parseInt(codeList.get(limit - 1).substring(start - 1, start + customCode.getSerialNum() - 1).trim()) <
//                Integer.parseInt(codeList.get(0).substring(start - 1, start + customCode.getSerialNum() - 1).trim())) {
//            throw new ServiceException(CustomCodeExceptionEnum.LENGTH_ERROR);
//        }
        // 编号数量校验
        if (codeList.size() != limit) {
            // 编号数量检验 若小于，尽最大努力补齐
            if (codeList.size() < limit) {
                // 需要生成的编号数量是否符合剩余的编号数量
//                if ((maxNum - max) < limit) {
//                    throw new ServiceException(CustomCodeExceptionEnum.LIMIT_ERROR);
//                }
                // 删除所有的编号，重新生成新的编号
//                codeList.clear();
                String numTem = "%0{}d";
                int maxNew = Integer.parseInt(codeList.get(codeList.size() - 1).substring(start - 1, start + customCode.getSerialNum() - 1).trim());
                for (int i = 0; i < limit - codeList.size(); i++) {
                    codeList.add(String.format(StrUtil.format(numTem, customCode.getSerialNum()), maxNew + i + 1));
                }
            }
        }
        // 编号重复性校验
        final int count = SpringUtil.getBean(iService).count(Wrappers.<T>query().in(logo, codeList));
        if (count > 0) {
            throw new ServiceException(CustomCodeExceptionEnum.CODE_ERROR);
        }
        return codeList;
    }

    /**
     * return: Boolean
     * Author: sxy
     * Description: //TODO 检查数据库里是否有相同的ID，true为没有，false为有
     * Date: 16:46 2022/6/27
     * Param: iService，code，logo
     **/
    private static <T> Boolean checkCodeByFactory(Class<? extends IService<T>> iService, String code, int logo) {
        // TODO 判空
        if (ObjectUtil.isEmpty(iService)) {
            throw new ServiceException(3, "ServiceImpl is null");
        }
        if (ObjectUtil.isEmpty(code)) {
            throw new ServiceException(3, "code is null");
        }
        if (ObjectUtil.isEmpty(logo)) {
            throw new ServiceException(3, "logo is null");
        }
        String myCode = codeMap.get(logo);
        if (ObjectUtil.isEmpty(myCode) || ObjectUtil.isNull(myCode)) {
            throw new ServiceException(3, "logo is error");
        }
        // TODO 条件构造器
        QueryWrapper<? extends BaseEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(myCode, code);
        // TODO 获取对应的Service
        IService service = SpringUtil.getBean(iService);
        int count = service.count(wrapper);
        // TODO 检查没有：true；否则：false
        if (count != 0) {
            return false;
        } else {
            return true;
        }
    }
}
